﻿using System;
using System.Linq;
using NUnit.Framework;

namespace OpenWaves.UnitTests
{
    [TestFixture]
    public class UrlTests
    {
        [Test]
        public void TestPathWithSpecialCharactersIsCorrectlySplitIntoSegments()
        {
            var url = Url.FromUri(new Uri("/elo%2Fmelo", UriKind.Relative));
            Assert.That(url.Path.Segments, Is.EquivalentTo(new[] {"elo/melo"}));
        }

        [Test]
        public void TestRelativeUrlUrlParsesWithRootedPath()
        {
            var url = Url.ParseRelative("/elo/melo");
            Assert.That(url.Path.ToString(), Is.EqualTo("/elo/melo"));
        }

        [Test]
        public void TestRelativeUrlParsesUrlWithRelativePath()
        {
            var url = Url.ParseRelative("elo/melo");
            Assert.That(url.Path.ToString(), Is.EqualTo("elo/melo"));
        }

        [Test]
        public void TestUrlRelativeUrlParsesUrlWithQueryString()
        {
            var url = Url.ParseRelative("/elo/melo?elo=1&melo=2");
            Assert.That(url.Query["elo"], Is.EqualTo("1"));
            Assert.That(url.Query["melo"], Is.EqualTo("2"));
        }

        [Test]
        public void TestParseDoesNotDecodeFragment()
        {
            var url = Url.Parse("/elo/melo#elo%20melo");
            Assert.That(url.Fragment, Is.EqualTo("elo%20melo"));

            url = Url.Parse("http://elomelo.com/elo/melo#elo%20melo");
            Assert.That(url.Fragment, Is.EqualTo("elo%20melo"));
        }

        [Test]
        public void TestUrlRelativeUrlParsesApplicationRelativeUrl()
        {
            var url = Url.ParseRelative("~/Lists/ImportantMessages/AllItems.aspx");
            Assert.That(url.Path.Segments.Count(), Is.EqualTo(4));
        }

        [Test]
        public void TestAbsoluteUrlParsesUrlWithNoScheme()
        {
            Assert.That(Url.ParseAbsolute("onet.pl").ToString(), Is.EqualTo("http://onet.pl/"));
        }

        [Test]
        public void TestAbsoluteUrlNormalizesHost()
        {
            var url = AbsoluteUrl.ParseAbsolute("http://MyHost.com/Welcome%20to%20the%20jungle/Track%201");

            Assert.That(url, Is.Not.Null);
            Assert.That(url.Host, Is.EqualTo("myhost.com"));
        }

        [Test]
        public void TestAbsoluteUrlParsesPathWithEncodedEntities()
        {
            var url = AbsoluteUrl.ParseAbsolute("http://myhost.com/Welcome%20to%20the%20jungle/Track%201");

            Assert.That(url, Is.Not.Null);
            Assert.That(url.Path.Segments.Count(), Is.EqualTo(2));
            Assert.That(url.Path.Segments.First(), Is.EqualTo("Welcome to the jungle"));
            Assert.That(url.Path.Segments.Skip(1).First(), Is.EqualTo("Track 1"));
        }

        [Test]
        public void TestAbsoluteUrlParsesUrlWithEncodedQuery()
        {
            var url = AbsoluteUrl.ParseAbsolute("http://myhost.com/path?Value%201=Some%20Value&Another%20Value=Some%20Other%20Value");

            Assert.That(url, Is.Not.Null);
            Assert.That(url.Query.Count, Is.EqualTo(2));
            Assert.That(url.Query["Value 1"], Is.EqualTo("Some Value"));
            Assert.That(url.Query["Another Value"], Is.EqualTo("Some Other Value"));
        }

        [Test]
        public void TestEncodingAbsoluteUrl()
        {
            const string path = "/related/item?url=http%3a%2f%2fwww.access-ecom.info%2farticle.cfm%3fid%3d71%26xid%3dMN";
            var urlBuilder = AbsoluteUrlBuilder.Create("http", "sample.com", Url.ParseRelative(path));

            Url url = urlBuilder.ToUrl();

            Assert.AreEqual("http://sample.com" + path, url.ToString());
        }

        [Test]
        public void TestEncodingRelativeUrl()
        {
            const string path = "/related/item?url=http%3a%2f%2fwww.access-ecom.info%2farticle.cfm%3fid%3d71%26xid%3dMN";
            var urlBuilder = RelativeUrlBuilder.From(Url.ParseRelative(path));

            Url url = urlBuilder.ToUrl();

            Assert.AreEqual(path, url.ToString());
        }

        [Test]
        public void TestUrlCorrectlyParsesUrlWithDuplicatedKeysInQueryString()
        {
            const string url = "http://onet.pl/page.html?x=test&x=test2";
            Url parsedUrl;

            Assert.IsTrue(Url.TryParse(url, out parsedUrl));
        }

        [Test]
        public void TestCombiningFileUrlToString()
        {
            const string rootUrl = "file://c:/temp";
            const string relativePath = "abc";
            Url url = Url.Parse(rootUrl);

            Assert.IsInstanceOf<FileUrl>(url);

            var combined = url.Combine(UrlPath.Parse(relativePath));
            Assert.AreEqual("file:///c:/temp/abc", combined.ToString());
        }

        [Test]
        public void TestCombiningFileUrlToUri()
        {
            const string rootUrl = "file://c:/temp";
            const string relativePath = "abc";
            Url url = Url.Parse(rootUrl);

            Assert.IsInstanceOf<FileUrl>(url);

            var combined = url.Combine(UrlPath.Parse(relativePath));
            Assert.AreEqual(new Uri("file:///c:/temp/abc"), ((Uri)combined));
        }

        [Test]
        public void TestUrlPortForUnknownSchema()
        {
            AbsoluteUrl url = (AbsoluteUrl)Url.Parse("svn://svn-repo");

            //unknown default port for schema
            Assert.AreEqual(-1, url.Port);

            AbsoluteUrlBuilder builder = AbsoluteUrlBuilder.From(url);
            AbsoluteUrl buildUrl = builder.ToUrl();
            //unknown default port for schema
            Assert.AreEqual(-1, buildUrl.Port);
        }

        [Test]
        public void TestWhenParsingAbsoluteUrlTrailingSlashRemains()
        {
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl:8080/");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/gasd/");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/gasd.txt/");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/gasd.txt/?12=20#a=12");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/?12=20#a=12");
        }

        [Test]
        public void TestWhenParsingAbsoluteUrlTrailingSlashIsNotAdded()
        {
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl:8080/");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/gasd");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/gasd.txt");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/gasd.txt?12=20#a=12");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/?12=20#a=12");
            AssertParsingDoesNotChangeTrailingSlash("http://onet.pl/?12=20#a");
        }

        [Test]
        public void TestWhenParsingRelativeUrlTrailingSlashRemains()
        {
            AssertParsingDoesNotChangeTrailingSlash("/");
            AssertParsingDoesNotChangeTrailingSlash("/asasd/");
            AssertParsingDoesNotChangeTrailingSlash("/asdas.txt/");
            AssertParsingDoesNotChangeTrailingSlash("/gasd.txt/?12=20#a12");
            AssertParsingDoesNotChangeTrailingSlash("/asdas.txt/?12=20");
        }

        [Test]
        public void TestWhenParsingRelativeTrailingSlashIsNotAdded()
        {
            AssertParsingDoesNotChangeTrailingSlash("/asasd");
            AssertParsingDoesNotChangeTrailingSlash("/asdas.txt");
            AssertParsingDoesNotChangeTrailingSlash("/gasd.txt/?12=20#a12");
            AssertParsingDoesNotChangeTrailingSlash("/asdas.txt?12=20");
        }

        [Test]
        public void TestAchorIsEncodedInAbsoluteUrl()
        {

            Assert.AreEqual(
                new Uri("http://onet.pl/gasd.txt/?12=20#a12").GetComponents(UriComponents.AbsoluteUri,
                                                                            UriFormat.UriEscaped),
                Url.Parse("http://onet.pl/gasd.txt/?12=20#a12").ToString());
            Assert.AreEqual(
                new Uri("http://onet.pl/gasd.txt/?12=20#a12=b?c<d\"e/f#g;h12").GetComponents(UriComponents.AbsoluteUri,
                                                                                             UriFormat.UriEscaped),
                Url.Parse("http://onet.pl/gasd.txt/?12=20#a12=b?c<d\"e/f#g;h12").ToString());

        }

        [Test]
        public void TestAchorIsEncodedInRelativeUrl()
        {
            //getcomponents is not available on relative url
            string s = "/gasd.txt/?12=20#a12";
            StringAssert.AreEqualIgnoringCase(
                    new Uri("http://onet.pl" + s).GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped),
                    "http://onet.pl" + Url.Parse(s));

            const string s2 = "/gasd.txt/?12=20#a12=b?c<>d\"e/f#g;h!i12";
            StringAssert.AreEqualIgnoringCase(

                new Uri("http://onet.pl" + s2).GetComponents(UriComponents.AbsoluteUri, UriFormat.UriEscaped),
                "http://onet.pl" + Url.Parse(s2));


        }

        private static void AssertParsingDoesNotChangeTrailingSlash(string input)
        {
            Url url = Url.Parse(input);
            Assert.AreEqual(input, url.ToString());
        }

        [Test]
        public void TestEncodingJapanQuery()
        {
            var absolute = Url.Parse("http://onet.pl/a.aspx");
            UrlBuilder absoluteBuilder = absolute.GetBuilder().AddParameter("search", "ノルウェー").AddParameter("search2", "asd");

            var relative = Url.Parse("/a.aspx");
            UrlBuilder relativeBuilder = relative.GetBuilder().AddParameter("search", "ノルウェー").AddParameter("search2", "asd");


            Assert.AreEqual(absoluteBuilder.ToString(), "http://onet.pl" + relativeBuilder.ToUrl());

        }

        [Test]
        public void TestSpaceIsNotEncodedAsPlusSign()
        {
            Assert.That(Url.Encode("Making Waves"), Is.EqualTo("Making%20Waves"));
        }

        [Test]
        public void TestPlusSignIsCorrectlyEncoded()
        {
            Assert.That(Url.Encode("Two+Two"), Is.EqualTo("Two%2bTwo"));
        }

        [Test]
        public void TestUrlCorrectlyEncodesSpaceCharacterInPathAndQuery()
        {
            var url = Url.Parse("http://google.com/Making Waves?Query Text=Hello World");
            Assert.That(url.ToString(), Is.EqualTo("http://google.com/Making%20Waves?Query%20Text=Hello%20World"));
        }

        [Test]
        public void TestParseMailToUrl()
        {
            var url = Url.Parse("mailto:jan.kowalski@server.com");
            Assert.AreEqual("mailto:jan.kowalski@server.com", url.ToString());
        }

        [Test]
        public void TestRelativeUrlWithFragmentAndEmptyPathIsParsed()
        {
            var url = Url.Parse("#fragment");

            Assert.That(url.Fragment, Is.EqualTo("fragment"));
            Assert.That(url.Path.ToString(), Is.Empty);
        }

        [Test]
        public void TestRelativeUrlSurvivesParseToStringRoundtrip()
        {
            var url = Url.Parse("/s1/s2?elo=melo#Elo%20Melo");
            Assert.AreEqual("/s1/s2?elo=melo#Elo%20Melo", url.ToString());
        }

        [Test]
        public void WhenResolvingAgainstUrlThenBaseUrlQueryIsRemoved()
        {
            Assert.That(Url.ParseRelative("/relative?aaa=bbb").ResolveAgainst(Url.ParseAbsolute("http://elo.melo/test?elo=melo")), 
                Is.EqualTo(Url.ParseAbsolute("http://elo.melo/relative?aaa=bbb")));
        }
    }
}